//============================================================================
// Name        : cpp_file.cpp
// Author      : darkflash
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <sys/termios.h>
#include <cstring>
#include <fstream>
#include <cstdlib>

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
using namespace std;

#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif

#ifndef TCSANOW
#define TCSANOW 0
#endif

struct termios t;

typedef unsigned char byte;

struct StructData
{
	int intValue;
	bool boolValue;
	char charValue[16];
};

class BoostData
{
public:
	int intValue;
	bool boolValue;
	string stringValue;
private:
	friend class boost::serialization::access;
	template <typename Archive>
	void serialize(Archive& ar, const unsigned int version)
	{
		ar& intValue;
		ar& boolValue;
		ar& stringValue;
	}
};

void fnOffBufferedInput()
{
	tcgetattr(STDIN_FILENO, &t); //get the current terminal I/O structure
	t.c_lflag &= ~ICANON; //Manipulate the flag bits to do what you want it to do
	tcsetattr(STDIN_FILENO, TCSANOW, &t); //Apply the new settings
}

void fnOnBufferedInput()
{
	tcgetattr(STDIN_FILENO, &t); //get the current terminal I/O structure
	t.c_lflag |= ICANON; //Manipulate the flag bits to do what you want it to do
	tcsetattr(STDIN_FILENO, TCSANOW, &t); //Apply the new settings
}

int getch(void)
{
	fnOffBufferedInput();
	int ch = getchar(); /* standard getchar call */
	fnOnBufferedInput();
	return ch; /*return received char */
}

void fnByteArrayWrite(const char* data, size_t dataLen)
{
	ofstream file("bytearray.txt", ios::out | ios::binary);
	//file << data;
	file.write(data, dataLen);
	file.flush();
	file.close();
}

void fnByteArrayRead(char* readData, size_t &dataLen)
{
	ifstream file("bytearray.txt", ios::in | ios::binary);
	file.seekg(0, file.end);
	dataLen = file.tellg();
	file.seekg(0, file.beg);

	char buffer[dataLen];
	readData = (char*)realloc(readData, dataLen * sizeof(char));

	while(!file.eof()){
		file.read(buffer, dataLen);
	}
	file.close();



	memcpy(readData, buffer, dataLen);
}

void fnByteArrayMode()
{
	char data[2] = {1,2};

	fnByteArrayWrite(data, sizeof(data)/sizeof(*data));
	char* readData = (char*)calloc(1, sizeof(char));
	size_t dataLen;
	fnByteArrayRead(readData, dataLen);

	for(int i=0;i<dataLen;i++){
		printf("%02x", readData[i]);
	}
	cout << endl;

	free(readData);
}

void fnBinaryWrite(int intValue, bool boolValue, string stringValue)
{
	ofstream file("binary.txt", ios::out | ios::binary);
	file.write(reinterpret_cast<char*>(&intValue), sizeof(intValue));
	file.write(reinterpret_cast<char*>(&boolValue), sizeof(boolValue));
	//file.write(reinterpret_cast<char*>(&stringValue), sizeof(stringValue));
	file << stringValue << endl;;
	file.flush();
	file.close();
}

void fnBinaryRead(int& intValue, bool& boolValue, string& stringValue)
{
	ifstream file("binary.txt", ios::in | ios::binary);
	while(!file.eof()){
		file.read(reinterpret_cast<char*>(&intValue), sizeof(int));
		file.read(reinterpret_cast<char*>(&boolValue), sizeof(bool));
		getline(file, stringValue);
		//file.read(reinterpret_cast<char*>(&stringValue), sizeof(string));
	}
	file.close();
}

void fnBinaryMode()
{
	int intValue = 1;
	bool boolValue = true;
	string stringValue = "my string";

	fnBinaryWrite(intValue, boolValue, stringValue);

	int readIntValue;
	bool readBoolValue;
	string readStringValue;

	fnBinaryRead(readIntValue, readBoolValue, readStringValue);

	cout << readIntValue << endl;
	cout << readBoolValue << endl;
	cout << readStringValue << endl;
}

void fnSerializeWrite(StructData structData)
{
	ofstream file("serialize.txt", ios::out | ios::binary);
	file.write(reinterpret_cast<char*>(&structData), sizeof(structData));
	file.flush();
	file.close();
}

void fnSerializeRead(StructData& structData)
{
	ifstream file("serialize.txt", ios::in | ios::binary);
	while(!file.eof()){
		file.read(reinterpret_cast<char*>(&structData), sizeof(structData));
	}
	file.close();
}

void fnSerializeMode()
{
	StructData structData;
	structData.intValue = 1;
	structData.boolValue = true;
	strncpy(structData.charValue, "my chars",9);

	fnSerializeWrite(structData);

	StructData readStructData;
	fnSerializeRead(readStructData);

	cout << readStructData.intValue << endl;
	cout << readStructData.boolValue << endl;
	cout << readStructData.charValue << endl;
}

void fnTextWrite(string input_text)
{
	ofstream file("text.txt", ios::out);
	file << input_text;
	file.flush();
	file.close();
}

void fnTextRead(string& readText)
{
	ifstream file("text.txt", ios::in);
	file >> readText;
	file.close();
	getchar();
}

void fnTextMode()
{
	string input_text;
	cout << "input text: ";
	cin >> input_text;

	fnTextWrite(input_text);

	string readText;
	fnTextRead(readText);

	cout << readText << endl;
}

void fnBoostSerializeWrite(BoostData data)
{
	ofstream file("boost.txt", ios::out | ios::binary);
	boost::archive::binary_oarchive oa(file);
	oa << data;
}

void fnBoostSerializeRead(BoostData& data)
{
	ifstream file("boost.txt", ios::in | ios::binary);
	boost::archive::binary_iarchive ia(file);
	ia >> data;
}

void fnBoostSerializeMode()
{
	BoostData data;
	data.intValue = 1;
	data.boolValue = true;
	data.stringValue = "my string";

	fnBoostSerializeWrite(data);

	BoostData readData;
	fnBoostSerializeRead(readData);

	cout << readData.intValue << endl;
	cout << readData.boolValue << endl;
	cout << readData.stringValue << endl;
}

void fnPrintMode()
{
	cout << "1. byte array" << endl;
	cout << "2. binary" << endl;
	cout << "3. serialize" << endl;
	cout << "4. text" << endl;
	cout << "5. boost::serialize" << endl;
	cout << "q. quit" << endl;
}

int main() {
	int input_mode;

	do{
		fnPrintMode();
		input_mode = getch();
		cout << endl;

		switch(input_mode){
		case '1':
			fnByteArrayMode();
			break;
		case '2':
			fnBinaryMode();
			break;
		case '3':
			fnSerializeMode();
			break;
		case '4':
			fnTextMode();
			break;
		case '5':
			fnBoostSerializeMode();
			break;
		}
	}while(input_mode != 'q');
	return 0;
}
